package com.xcdh.commons.query;

import com.baomidou.mybatisplus.annotation.OrderBy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDto;
import lombok.Data;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

@Data
public class QueryPayload  {
    private Long page = 1L;
    private Long pageSize =20L;

    private List<QueryItemPayload> items ;

    public <T> Page<T> getPageDto(Class<T> clazz){
        return PageDto.of(this.page , this.pageSize);
    }

    public <T> QueryWrapper<T> getQueryWrapper(Class<T> clazz , Map<String , String> columnTrMap , boolean isOrderBy){
        QueryWrapper<T> queryWrapper = new QueryWrapper<>() ;
        if(items != null){
            Map<String, String> columnMap = buildColumnMap(clazz);
            for (QueryItemPayload item : items) {
                String column = getColumn(columnMap , item.getKey() , columnTrMap) ;
                if(QuerySqlKeyword.EQ == item.getOp()){
                    queryWrapper.eq(column ,item.getValue() );
                }else if(QuerySqlKeyword.LIKE == item.getOp()){
                    queryWrapper.like(column ,item.getValue() );
                }else if(QuerySqlKeyword.NOT_LIKE == item.getOp()){
                    queryWrapper.notLike(column ,item.getValue() );
                }else if(QuerySqlKeyword.IN == item.getOp()){
                    queryWrapper.in(column , item.getInValue());
                }else if(QuerySqlKeyword.NOT_IN == item.getOp()){
                    queryWrapper.notIn(column ,item.getValue() );
                }else if(QuerySqlKeyword.RLIKE == item.getOp()){
                    queryWrapper.likeRight(column ,item.getValue() );
                }else if(QuerySqlKeyword.L_LIKE == item.getOp()){
                    queryWrapper.likeLeft(column ,item.getValue() );
                }else if(QuerySqlKeyword.BETWEEN == item.getOp()){
                    if(item.getValue() != null){
                        String[] split = item.getValue().toString().split(",");
                        queryWrapper.between(column , split[0] , split[1]) ;
                    }
                }else if(QuerySqlKeyword.LT == item.getOp()){
                    queryWrapper.lt(column , item.getValue()) ;
                }else if(QuerySqlKeyword.GE == item.getOp()){
                    queryWrapper.ge(column , item.getValue()) ;
                }else if(QuerySqlKeyword.LE == item.getOp()){
                    queryWrapper.le(column , item.getValue()) ;
                }else if(QuerySqlKeyword.GT == item.getOp()){
                    queryWrapper.gt(column , item.getValue()) ;
                }
            }
        }
        if(isOrderBy){
            handlerOrderBy(clazz , queryWrapper , columnTrMap) ;
        }
        return queryWrapper ;
    }
    public <T> QueryWrapper<T> getQueryWrapper(Class<T> clazz){
        return getQueryWrapper(clazz , null , true) ;
    }
    public <T> QueryWrapper<T> getQueryWrapper(Class<T> clazz ,boolean isOrderBy){
        return getQueryWrapper(clazz , null , isOrderBy) ;
    }

    public void handlerOrderBy(Class<?> clazz , QueryWrapper<?> queryWrapper , Map<String , String> columnTrMap){
        Field[] declaredFields = clazz.getDeclaredFields();
        List<QueryItemPayload> queryItem = getOrderByQueryItem();
        Map<String, String> columnMap = buildColumnMap(clazz);
        if(queryItem.isEmpty()){
            List<Field> fieldList = Arrays.stream(declaredFields).filter(field -> field.getAnnotation(OrderBy.class) != null)
                    .sorted(Comparator.comparingInt(f -> f.getAnnotation(OrderBy.class).sort())).collect(Collectors.toList());
            for (Field field : fieldList) {
                OrderBy annotation = field.getAnnotation(OrderBy.class);
                String column = getColumn(columnMap, field.getName() , columnTrMap);
                queryWrapper.orderBy(true ,  !annotation.isDesc() , column) ;
            }
        }else{
            for (QueryItemPayload itemPayload : queryItem) {
                String column = getColumn(columnMap , itemPayload.getKey() , columnTrMap) ;
                if(columnTrMap != null){
                    String trColumn = columnTrMap.get(column);
                    column = trColumn == null ? column : trColumn ;
                }
                queryWrapper.orderBy(true ,  itemPayload.getOp() != QuerySqlKeyword.DESC, column) ;
            }
        }

    }

    public List<QueryItemPayload> getOrderByQueryItem(){
        if(items == null){
            return new ArrayList<>() ;
        }
       return items.stream()
               .filter(item->item.getOp() == QuerySqlKeyword.ASC || item.getOp() == QuerySqlKeyword.DESC)
               .collect(Collectors.toList()) ;
    }
    public String getColumn(Map<String, String> columnMap , String key ,  Map<String , String> columnTrMap){
        String column = columnMap.get(key) ;
        if(columnTrMap != null){
            if(column.startsWith("`")){
                column = column.replaceAll("`", "") ;
            }
            String trColumn = columnTrMap.get(column);
            column = trColumn == null ? column : trColumn ;
        }
        return column == null ? camelToSnake(key) : camelToSnake(column) ;
    }

    public Map<String,String> buildColumnMap(Class<?> clazz){
        Field[] declaredFields = clazz.getDeclaredFields();
        Map<String,String> fieldColumnMap = new HashMap<>() ;
        for (Field field : declaredFields) {
            TableField tableField = field.getAnnotation(TableField.class);
            if(tableField == null){
                fieldColumnMap.put(field.getName() , field.getName()) ;
            }else{
                fieldColumnMap.put(field.getName() , tableField.value()) ;
            }
        }
        return fieldColumnMap ;
    }

    public static String camelToSnake(String camelStr) {
        StringBuilder snakeStr = new StringBuilder();
        for (int i = 0; i < camelStr.length(); i++) {
            char c = camelStr.charAt(i);
            if (i > 0 && Character.isUpperCase(c)) {
                snakeStr.append('_');
            }
            snakeStr.append(Character.toLowerCase(c));
        }
        return snakeStr.toString();
    }
}
